package com.cqhfan.authservice.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.cqhfan.api.clients.user.UserClient;
import com.cqhfan.authservice.service.AuthService;
import com.cqhfan.authservice.service.TokenService;
import com.cqhfan.core.constans.BizConstants;
import com.cqhfan.core.domain.dto.UserAuthDTO;
import com.cqhfan.core.exceptions.BizException;
import com.cqhfan.core.utils.MD5Util;
import com.cqhfan.pojo.user.dto.UserLoginDTO;
import com.cqhfan.pojo.user.po.User;
import com.cqhfan.pojo.user.vo.UserLoginVO;
import com.cqhfan.redis.constants.RedisConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 眭传洪
 * @create 2025-07-25 1:56
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final StringRedisTemplate stringRedisTemplate;

    private final UserClient userClient;

    private final TokenService tokenService;

    @Override
    public void sendMessageCode(String phone) {
        // 校验手机号
        if (StrUtil.isBlank(phone)) {
            throw new BizException("手机号为空");
        }

        // 校验是否间隔小于一分钟
        String codeIntervalKey = RedisConstants.CODE_INTERVAL_KEY_PREFIX + phone;
        Boolean exists = stringRedisTemplate.hasKey(codeIntervalKey);
        if (BooleanUtil.isTrue(exists)) {
            throw new BizException("验证码发送太频繁");
        }

        // 创建验证码
        String code = RandomUtil.randomNumbers(BizConstants.VERIFY_CODE_LENGTH);

        // 保存到redis
        String codeSaveKey = RedisConstants.PHONE_CODE_KEY_PREFIX + phone;
        stringRedisTemplate.opsForValue().set(codeSaveKey, code, RedisConstants.PHONE_CODE_TTL, TimeUnit.MINUTES);
        stringRedisTemplate.opsForValue().set(codeIntervalKey, "", RedisConstants.CODE_INTERVAL_TTL, TimeUnit.MINUTES);

        // TODO 调用云服务发送

        // 记录日志
        log.info("{} 发送短信成功，验证码：{}", phone, code);
    }

    @Override
    @Transactional
    public UserLoginVO userLogin(UserLoginDTO loginBody) {
        String code = loginBody.getCode();
        String password = loginBody.getPassword();
        String phone = loginBody.getPhone();
        String userName = loginBody.getUserName();

        // 参数校验
        if (StrUtil.hasBlank(code, phone) && StrUtil.hasBlank(userName, password)) {
            throw new BizException("登录参数未填写完整");
        }

        // 分别处理获取登录用户信息
        User user;
        if (StrUtil.isBlank(phone)) {
            // 用户名密码登录
            user = loginByUserNameAndPassword(userName, password);
        } else {
            // 手机号登录
            user = loginByPhoneAndCode(phone, code);
        }

        // 将登录用户信息存储到redis
        String uuid = UUID.fastUUID().toString(true);
        String key = RedisConstants.LOGIN_CACHE_KEY_PREFIX + uuid;
        UserAuthDTO loginAuthBody = new UserAuthDTO();
        loginAuthBody.setUserId(user.getId());
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(loginAuthBody),
                tokenService.getAccessTokenTTL(), TimeUnit.MINUTES);

        // 封装返回token
        Map<String, Object> map = new HashMap<>();
        map.put(BizConstants.TOKEN_LOGIN_INFO, uuid);
        return UserLoginVO.builder()
                .accessToken(tokenService.createAccessToken(map))
                .refreshToken(tokenService.createRefreshToken(map, loginAuthBody))
                .build();
    }

    private User loginByPhoneAndCode(String phone, String code) {
        // 获取验证码
        String key = RedisConstants.PHONE_CODE_KEY_PREFIX + phone;
        String cacheCode = stringRedisTemplate.opsForValue().get(key);

        // 校验验证码
        if (StrUtil.isBlank(cacheCode)) {
            throw new BizException("验证码已过期");
        }
        if (!cacheCode.equals(code)) {
            throw new BizException("验证码错误");
        }

        // 获取登录用户信息
        User user = userClient.getByUserPhone(phone);
        if (user == null) {
            // 这里需要注册
            user = new User();
            user.setPhone(phone);
            user.setNickName(BizConstants.NICK_NAME_PREFIX + UUID.fastUUID().toString(true));
            key = RedisConstants.DOUYIN_NUMBER_INCR_KEY;
            Long number = stringRedisTemplate.opsForValue().increment(key, 1);
            user.setDouyinNumber(number);
            user.setStatus(Boolean.TRUE);
            user.setAvatar("http://192.168.150.101:9000/douyin/90d3503f-bd31-4d0b-8191-fbf467795154.png");
            user.setBackgroundUrl("http://192.168.150.101:9000/douyin/90d3503f-bd31-4d0b-8191-fbf467795154.png");
            user.setCreateTime(LocalDateTime.now());
            Long userId = userClient.save(user);
            user.setId(userId);

            // 初始化首页几个数量的值
            key = RedisConstants.UserProfileInfo.KEY + user.getId();
            Map<String, String> map = new HashMap<>();
            map.put(RedisConstants.UserProfileInfo.LIKED_HASH_KEY, "0");
            map.put(RedisConstants.UserProfileInfo.FRIEND_HASH_KEY, "0");
            map.put(RedisConstants.UserProfileInfo.FANS_HASH_KEY, "0");
            map.put(RedisConstants.UserProfileInfo.FOLLOW_HASH_KEY, "0");
            stringRedisTemplate.opsForHash().putAll(key, map);
        }
        return user;
    }

    private User loginByUserNameAndPassword(String userName, String password) {
        // 查询用户
        User user = userClient.getByUserName(userName);
        if (user == null) {
            throw new BizException("登录失败，用户不存在");
        }

        // 校验是否已停用
        if (!user.getStatus()) {
            throw new BizException("账户已停用");
        }

        // 校验密码
        String saltPassword = MD5Util.encodeWithSalt(password, user.getSalt());
        if (!saltPassword.equals(user.getPassword())) {
            throw new BizException("登录失败，密码错误");
        }
        return user;
    }

    @Override
    public UserLoginVO refreshToken(String refreshToken) {
        return tokenService.refreshToken(refreshToken);
    }
}
